package config

import (
	"fmt"
	"io"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/rs/zerolog"
	"github.com/rs/zerolog/pkgerrors"
	"gopkg.in/natefinch/lumberjack.v2"
)

// 怎么配置
// 是否要打印到控制台
// 是否要打印到文件里,是否要轮转 100M 5
type Log struct {
	// 0 为打印日志全路径, 默认打印3层路径
	CallerDeep int `toml:"caller_deep" json:"caller_deep" yaml:"caller_deep"  env:"CALLER_DEEP"`
	// 日志的级别, 默认Debug
	Level zerolog.Level `toml:"level" json:"level" yaml:"level"  env:"LEVEL"`

	// 控制台日志配置
	Console Console `toml:"console" json:"console" yaml:"console" envPrefix:"CONSOLE_"`
	// 日志文件配置
	File File `toml:"file" json:"file" yaml:"file" envPrefix:"FILE_"`

	root *zerolog.Logger
	lock sync.Mutex
}

type Console struct {
	Enable  bool `toml:"enable" json:"enable" yaml:"enable"  env:"ENABLE"`
	NoColor bool `toml:"no_color" json:"no_color" yaml:"no_color"  env:"NO_COLOR"`
}

func (c *Console) ConsoleWriter() io.Writer {
	output := zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
		w.NoColor = c.NoColor
		w.TimeFormat = time.RFC3339
	})

	output.FormatLevel = func(i interface{}) string {
		return strings.ToUpper(fmt.Sprintf("%-6s", i))
	}
	output.FormatMessage = func(i interface{}) string {
		return fmt.Sprintf("%s", i)
	}
	output.FormatFieldName = func(i interface{}) string {
		return fmt.Sprintf("%s:", i)
	}
	output.FormatFieldValue = func(i interface{}) string {
		return strings.ToUpper(fmt.Sprintf("%s", i))
	}
	return output
}

type File struct {
	// 是否开启文件记录
	Enable bool `toml:"enable" json:"enable" yaml:"enable"  env:"ENABLE"`
	// 文件的路径
	FilePath string `toml:"file_path" json:"file_path" yaml:"file_path"  env:"PATH"`
	// 单位M, 默认100M
	MaxSize int `toml:"max_size" json:"max_size" yaml:"max_size"  env:"MAX_SIZE"`
	// 默认保存 6个文件
	MaxBackups int `toml:"max_backups" json:"max_backups" yaml:"max_backups"  env:"MAX_BACKUPS"`
	// 保存多久
	MaxAge int `toml:"max_age" json:"max_age" yaml:"max_age"  env:"MAX_AGE"`
	// 是否压缩
	Compress bool `toml:"compress" json:"compress" yaml:"compress"  env:"COMPRESS"`
}

func (f *File) FileWriter() io.Writer {
	return &lumberjack.Logger{
		Filename:   f.FilePath,
		MaxSize:    f.MaxSize,
		MaxAge:     f.MaxAge,
		MaxBackups: f.MaxBackups,
		Compress:   f.Compress,
	}
}

func (l *Log) Logger() *zerolog.Logger {
	l.lock.Lock()
	defer l.lock.Unlock()

	if l.root == nil {
		var writers []io.Writer
		if l.Console.Enable {
			writers = append(writers, l.Console.ConsoleWriter())
		}
		if l.File.Enable {
			if l.File.FilePath == "" {
				l.File.FilePath = "logs/app.log"
			}
			writers = append(writers, l.File.FileWriter())
		}

		zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack

		// logger对象配置一些上下文信息: 2024-11-23T15:11:42+08:00
		// 带上中间信息Any
		// book/controller/book.go:60
		root := zerolog.New(io.MultiWriter(writers...)).With().Timestamp().Any("服务", "book-api")
		if l.CallerDeep > 0 {
			// 启用caller
			root = root.Caller()
			// 配置自定义caller
			zerolog.CallerMarshalFunc = l.CallerMarshalFunc
		}
		// 带有这些上下文信息的Logger对象 提前出来，作为全局Logger对象，供全局使用
		l.SetRoot(root.Logger().Level(l.Level))
	}

	return l.root
}

func (m *Log) SetRoot(v zerolog.Logger) {
	m.root = &v
}

// /go/src/your_project/some_file
// 21
func (m *Log) CallerMarshalFunc(pc uintptr, file string, line int) string {
	if m.CallerDeep == 0 {
		return file
	}

	short := file
	count := 0
	for i := len(file) - 1; i > 0; i-- {
		if file[i] == '/' {
			short = file[i+1:]
			count++
		}

		if count >= m.CallerDeep {
			break
		}
	}
	file = short
	return file + ":" + strconv.Itoa(line)
}
